package com.example.redissession.dlm;

import lombok.extern.slf4j.Slf4j;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * Distribute Lock Manager
 *
 * @author: cph
 * @date: 2022/3/22 10:53
 */
@Slf4j
public class DLMHelper {
    private final String lockKey;
    private final TimeUnit unit = TimeUnit.SECONDS;
    private final DLM dlm;
    private String lockType = "redisDLM";
    private String lockValue;
    private Long timeout = 5L;
    private int maxRetryCount = 5;

    public DLMHelper(String lockKey, Long timeout) {
        this.lockKey = lockKey;
        this.lockValue = UUID.randomUUID().toString();
        this.timeout = timeout;
        this.dlm = SpringContextUtil.getBean(lockType, DLM.class);
    }

    public DLMHelper(String lockKey) {
        this.lockKey = lockKey;
        this.lockValue = UUID.randomUUID().toString();
        this.dlm = SpringContextUtil.getBean(lockType, DLM.class);
    }

    public DLMHelper lock() {
        refreshLockValue();
        log.info("start lock lockKey:{}, lockValue:{}", lockKey, lockValue);
        int retryCount = 0;
        while (!dlm.lock(lockKey, lockValue, timeout, unit)) {
            log.info("获取锁失败，lockKey:{}, retryCount:{}", lockKey, retryCount);
            sleep(1000L);
            retryCount += 1;
            if (retryCount > maxRetryCount) {
                log.error("获取锁次数超过上限, 请检查, retryCount:{}", retryCount);
                throw new RuntimeException("获取锁次数超过上限，请检查");
            }
            log.info("开始第{}次重新获取锁, lockKey:{}", retryCount, lockKey);
        }
        log.info("lock success");
        return this;
    }

    public boolean tryLock() {
        try {
            return dlm.lock(lockKey, lockValue, timeout, unit);
        } catch (Exception e) {
            log.warn("获取锁异常: " + e.getMessage());
        }
        return false;
    }

    public void unlock() {
        log.info("start unlock lockKey:{}, lockValue:{}", lockKey, lockValue);
        try {
            dlm.unLock(lockKey, lockValue);
            log.info("unlock success, lockKey:{}", lockKey);
        } catch (Exception e) {
            log.warn("释放锁发生异常，等待锁过期释放:{}", lockKey);
        }
    }

    void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            log.error("sleep发生中断异常");
            throw new RuntimeException("sleep发生中断异常");
        }
    }

    void refreshLockValue() {
        this.lockValue = UUID.randomUUID().toString();
        log.info("refresh lock value:{}", this.lockValue);
    }

}
